import { ValidateTypes } from './enum';
import { isNumber, isString } from './utils';

function validString(param: unknown, key: string) {
    if (!isString(param)) {
        throw new Error(`Param ${key} must be type String!`);
    }
    return true;
}

function validNumber(param: unknown, key: string) {
    if (!isNumber(param)) {
        throw new Error(`Param ${key} must be type Number!`);
    }
    return true;
}

function validRequired(param: Record<string, any>, keys: string[]) {
    const copyParams: Record<string, any> = {};
    keys.forEach((key) => {
        copyParams[key] = param[key];
    });
    for (const key in copyParams) {
        if (!copyParams[key]) throw new Error(`Param ${key} must be required!`);
    }
    return true;
}

function validBool(param: unknown, key: string) {
    if (param !== true && param !== false) {
        throw new Error(`Param ${key} must be type Boolean!`);
    }
}

function doValid(options: {
    param: Record<string, any>;
    numberKeys: string[];
    stringKeys: string[];
    requiredKeys: string[];
    boolKeys: string[];
}) {
    const { param, numberKeys, stringKeys, requiredKeys, boolKeys } = options;
    validRequired(param, requiredKeys);
    for (const key in param) {
        if (numberKeys?.includes(key)) {
            validNumber(param[key], key);
        }
        if (stringKeys?.includes(key)) {
            validString(param[key], key);
        }
        if (boolKeys?.includes(key)) {
            validBool(param[key], key);
        }
    }
}

/**
 * 验证装饰器
 * @param validator 
 * @returns 
 */
export function Validate(validator?: (...args: any) => boolean) {
    return function (target: any, key: string, descriptor: PropertyDescriptor) {
        const oringinMethod = descriptor.value;
        const requiredKeys = Reflect.getMetadata(`${ValidateTypes.isRequired}_${key}`, target);
        const numberKeys = Reflect.getMetadata(`${ValidateTypes.isNumber}_${key}`, target);
        const stringKeys = Reflect.getMetadata(`${ValidateTypes.isString}_${key}`, target);
        const boolKeys = Reflect.getMetadata(`${ValidateTypes.isBool}_${key}`, target);

        descriptor.value = function () {
            // eslint-disable-next-line prefer-rest-params
            const params = [...arguments];
            doValid({
                param: params[0],
                numberKeys,
                stringKeys,
                requiredKeys,
                boolKeys,
            });
            if (validator) {
                const res = validator(params[0]);
                if (!res) {
                    throw new Error(`Param is not validate!`);
                }
            }
            return oringinMethod.apply(this, [...params]);
        };
    };
}

/**
 * 类型收集装饰器工厂
 * @param keyPrefix 
 * @param paramKeys 
 * @returns 
 */
function ValidTypeFactory(keyPrefix: string, paramKeys: string[]) {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    return function (target: object, propertyKey: string, _parameterIndex: number) {
        const defineKey = `${keyPrefix}_${propertyKey}`;
        Reflect.defineMetadata(defineKey, paramKeys, target);
    };
}

export function IsRequired(paramKeys: string[]) {
    return ValidTypeFactory(ValidateTypes.isRequired, paramKeys);
}

export function IsString(paramKeys: string[]) {
    return ValidTypeFactory(ValidateTypes.isString, paramKeys);
}

export function IsNumber(paramKeys: string[]) {
    return ValidTypeFactory(ValidateTypes.isNumber, paramKeys);
}

export function IsBool(paramKeys: string[]) {
    return ValidTypeFactory(ValidateTypes.isBool, paramKeys);
}
